package cn.btks.zuulserver.config.security;

import cn.btks.commonserver.result.ResultState;
import cn.btks.commonserver.result.StateInfo;
import cn.btks.zuulserver.dto.User;
import cn.btks.zuulserver.feign.service.backmanage.BackManageClient;
import cn.btks.zuulserver.feign.service.impl.UserClientImpl;
import cn.btks.zuulserver.jwt.JwtTokenFilter;
import cn.btks.zuulserver.security.AuthenticationAccessDeniedHandler;
import cn.btks.zuulserver.security.UrlAccessDecisionManager;
import cn.btks.zuulserver.security.UrlFilterInvocationSecurityMetadataSource;
import cn.btks.zuulserver.util.DecryptTel;
import cn.btks.zuulserver.util.JwtUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.*;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.util.StringUtils;

import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

/**
 * @author 26612
 */
@Slf4j
@EnableWebSecurity
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    UserClientImpl userServiceImpl;

    @Autowired
    UrlFilterInvocationSecurityMetadataSource urlFilterInvocationSecurityMetadataSource;

    @Autowired
    UrlAccessDecisionManager urlAccessDecisionManager;

    @Autowired
    AuthenticationAccessDeniedHandler authenticationAccessDeniedHandler;

    @Autowired
    Sm2PasswordEncoder sm2PasswordEncoder;

    @Autowired
    JwtTokenFilter jwtTokenFilter;

    @Autowired
    BackManageClient backManageClient;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//        auth.userDetailsService(userServiceImpl).passwordEncoder(new BCryptPasswordEncoder());
        auth.userDetailsService(userServiceImpl).passwordEncoder(sm2PasswordEncoder);
    }

    @Bean
    PasswordEncoder passwordEncoder() {
        return sm2PasswordEncoder;
//        return new BCryptPasswordEncoder();
    }

    @Override
    public void configure(WebSecurity web) {
        web.ignoring()
                .antMatchers("/index.html")
//                .antMatchers("/manage-server/api/v1.0/menu/")
//                .antMatchers("/manage-server/api/v1.0/user/")
//                .antMatchers("/database-handle-server/api/v1.0/backUpConfig/")
//                .antMatchers("/result-integration-server/api/v1.0/")
                .antMatchers("/swagger-ui.html")
                .antMatchers("/**/static/**")
                .antMatchers("/webjars/**")
                .antMatchers("/**/js/**")
                .antMatchers("/**/iserver/**")
                .antMatchers(("/**/css/**/"))
                .antMatchers(("/**/js/**/"))
                .antMatchers(("/**/img/**/"))
                .antMatchers(("/**/fonts/**/"))
                .antMatchers(("/**/*.pdf"))
                .antMatchers(("/**/*.png"))
                .antMatchers(("/**/*.html"))
                .antMatchers(("/**/*.ico"))
                .antMatchers(("/**/*.jpeg"))
                .antMatchers("/*/v2/api-docs",
                        "/doc.html",
                        "/swagger-resources/configuration/ui",
                        "/swagger-resources",
                        "/swagger-resources/configuration/security",
                        "/swagger-ui.html")
                .antMatchers("/*/swagger-ui.html/**").antMatchers("/*/swagger-resources/**");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
//                .antMatchers("/manage-server/api/v1.0/menu/").permitAll()
//                .antMatchers("/manage-server/api/v1.0/user/").permitAll()
//                .antMatchers("/database-handle-server/api/v1.0/backUpConfig/").permitAll()
//                .antMatchers("/result-integration-server/api/v1.0/").permitAll()
                .antMatchers("/swagger-ui.html").permitAll()
                .antMatchers("/webjars/**").permitAll()
                .antMatchers("/**/js/**").permitAll()
                .antMatchers(("/**/css/**/")).permitAll()
                .antMatchers(("/**/js/**/")).permitAll()
                .antMatchers(("/**/img/**/")).permitAll()
                .antMatchers(("/**/fonts/**/")).permitAll()
                .antMatchers(("/**/static/**/")).permitAll()
                .antMatchers(("/**/*.pdf")).permitAll()
                .antMatchers(("/**/*.png")).permitAll()
                .antMatchers(("/**/*.html")).permitAll()
                .antMatchers(("/**/*.ico")).permitAll()
                .antMatchers(("/**/*.jpeg")).permitAll()
                //通过 withObjectPostProcessor 将刚刚创建的 UrlFilterInvocationSecurityMetadataSource 和 UrlAccessDecisionManager 注入进来,
                // 到时候，请求都会经过刚才的过滤器（除了 configure(WebSecurity web)方法忽略的请求）
                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                    @Override
                    public <O extends FilterSecurityInterceptor> O postProcess(O o) {
                        o.setSecurityMetadataSource(urlFilterInvocationSecurityMetadataSource);
                        o.setAccessDecisionManager(urlAccessDecisionManager);
                        return o;
                    }
                })
                .and()
                .formLogin()
                .loginPage("/index.html#/login")
                .loginProcessingUrl("/doLogin/login")
                .usernameParameter("username")
                .passwordParameter("password")
                .successHandler((request, response, authentication) -> {
                    response.setContentType("application/json;charset=utf-8");
                    PrintWriter out = response.getWriter();
                    User user = (User) authentication.getPrincipal();
                    user.setPassword(null);
                    Map<String, Object> map = new HashMap<>();
                    map.put("user", user);
                    map.put("token", JwtUtil.generateToken(user));
                    map.put("menu", backManageClient.findAllByUserId(user.getId()).getData());
                    ResultState ok = new ResultState(StateInfo.LOGIN_OK.getCode(), StateInfo.LOGIN_OK.getMsg(), map);
                    out.write(new ObjectMapper().writeValueAsString(ok));
                    out.flush();
                    out.close();
                    backManageClient.recordLoginCount(user.getUsername(), StringUtils.isEmpty(user.getName()) ? "" : user.getName());
                })
                .failureHandler((request, response, authenticationException) -> {
                    response.setContentType("application/json;charset=utf-8");
                    PrintWriter out = response.getWriter();
                    ResultState respBean = new ResultState(StateInfo.LOGIN_ERROR.getCode(), StateInfo.LOGIN_ERROR.getMsg());
                    if (authenticationException instanceof LockedException) {
                        respBean.setMessage(StateInfo.LOGIN_LOCKED.getMsg());
                    } else if (authenticationException instanceof CredentialsExpiredException) {
                        respBean.setMessage(StateInfo.CREDENTIALS_EXPIRED.getMsg());
                    } else if (authenticationException instanceof AccountExpiredException) {
                        respBean.setMessage(StateInfo.ACCOUNT_EXPIRED.getMsg());
                    } else if (authenticationException instanceof DisabledException) {
                        respBean.setMessage(StateInfo.ACCOUNT_DISABLED.getMsg());
                    } else if (authenticationException instanceof BadCredentialsException) {
                        respBean.setMessage(StateInfo.LOGIN_USER_ERROR.getMsg());
                    }
                    log.error(respBean.getMessage());
                    out.write(new ObjectMapper().writeValueAsString(respBean));
                    out.flush();
                    out.close();
                })
                .permitAll().and()
                .httpBasic()
                .and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .cors().and().csrf().disable()
                .exceptionHandling().accessDeniedHandler(authenticationAccessDeniedHandler);
        http.addFilterAt(jwtTokenFilter, UsernamePasswordAuthenticationFilter.class);
    }

}
